package com.txy.infrastructure.persistent.repository;

import com.txy.domain.strategy.model.entity.StrategyAwardEntity;
import com.txy.domain.strategy.model.entity.StrategyEntity;
import com.txy.domain.strategy.model.entity.StrategyRuleEntity;
import com.txy.domain.strategy.model.vo.*;
import com.txy.domain.strategy.repository.IStrategyRepository;
import com.txy.infrastructure.persistent.dao.*;
import com.txy.infrastructure.persistent.po.*;
import com.txy.infrastructure.persistent.redis.IRedisService;
import com.txy.types.common.Constants;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RMap;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Repository
public class StrategyRepository implements IStrategyRepository {
    @Resource
    private IStrategyDao strategyDao;

    @Resource
    private IStrategyAwardDao strategyAwardDao;

    @Resource
    private IStrategyRuleDao strategyRuleDao;

    @Resource
    private IRedisService redisService;

    @Resource
    private IRuleTreeDao ruleTreeDao;

    @Resource
    private IRuleTreeNodeDao ruleTreeNodeDao;

    @Resource
    private IRuleTreeNodeLineDao ruleTreeNodeLineDao;

    @Override
    public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {
        // 先从缓存中获取
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_LIST_KEY + strategyId;
        List<StrategyAwardEntity> strategyAwardEntities = redisService.getValue(cacheKey);

        if (strategyAwardEntities != null && !strategyAwardEntities.isEmpty()) {
            return strategyAwardEntities;
        }

        // 从数据库中获取
        List<StrategyAward> strategyAwards = strategyAwardDao.queryStrategyAwardListByStrategyId(strategyId);
        strategyAwardEntities = new ArrayList<>();
        for (StrategyAward strategyAward : strategyAwards) {
            StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
                        .strategyId(strategyAward.getStrategyId())
                        .awardId(strategyAward.getAwardId())
                        .awardTitle(strategyAward.getAwardTitle())
                        .awardSubtitle(strategyAward.getAwardSubtitle())
                        .awardCount(strategyAward.getAwardCount())
                        .awardCountSurplus(strategyAward.getAwardCountSurplus())
                        .awardRate(strategyAward.getAwardRate())
                        .sort(strategyAward.getSort())
                        .build();
            strategyAwardEntities.add(strategyAwardEntity);
        }

        // 保存redis
        redisService.setValue(cacheKey, strategyAwardEntities);
        return strategyAwardEntities ;
    }

    @Override
    public void storeStrategyAwardSearchRateTable(String key, Integer rateRange, HashMap<Integer, Integer> shuffleStrategyAwardSearchRateTables) {
        // 1. 保存策略奖品概率范围，如10000 用于生成随机数
        redisService.setValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key, rateRange);

        // 2. 保存策略奖品概率查找表
        RMap<Integer, Integer> cacheRateTable = redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key);
        cacheRateTable.putAll(shuffleStrategyAwardSearchRateTables);
    }

    @Override
    public Integer getRateRange(String key) {
        return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key);
    }

    @Override
    public Integer getStragegyAwardAssemble(String strategyId, Integer randomNum) {
        return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + strategyId, randomNum);
    }

    @Override
    public StrategyEntity queryStrategyByStrategyId(Long strategyId) {
        String cacheKey = Constants.RedisKey.STRATEGY_KEY + strategyId;
        StrategyEntity strategyEntity = redisService.getValue(cacheKey);
        if (strategyEntity != null) {
            return strategyEntity;
        }
        Strategy strategy = strategyDao.queryStrategyByStrategyId(strategyId);
        strategyEntity = StrategyEntity.builder()
            .strategyId(strategy.getStrategyId())
            .strategyDesc(strategy.getStrategyDesc())
            .ruleModels(strategy.getRuleModels())
            .build();
        redisService.setValue(cacheKey, strategyEntity);
        return strategyEntity;
    }

    @Override
    public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel) {
//        String cacheKey = Constants.RedisKey.STRATEGY_RULE_KEY + strategyId + "_" + ruleModel;
//        StrategyRuleEntity strategyRuleEntity = redisService.getValue(cacheKey);
//        if (strategyRuleEntity != null) {
//            return strategyRuleEntity;
//        }
        StrategyRule strategyRule = new StrategyRule();
        strategyRule.setStrategyId(strategyId);
        strategyRule.setRuleModel(ruleModel);
        StrategyRule strategyRuleRes = strategyRuleDao.queryStrategyRule(strategyId, ruleModel);
        //        redisService.setValue(cacheKey, strategyRuleEntity);
        return StrategyRuleEntity.builder()
                .strategyId(strategyRuleRes.getStrategyId())
                .awardId(strategyRuleRes.getAwardId())
                .ruleType(strategyRuleRes.getRuleType())
                .ruleModel(strategyRuleRes.getRuleModel())
                .ruleValue(strategyRuleRes.getRuleValue())
                .ruleDesc(strategyRuleRes.getRuleDesc())
                .build();
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
        StrategyRule strategyRule = new StrategyRule();
        strategyRule.setStrategyId(strategyId);
        strategyRule.setRuleModel(ruleModel);
        strategyRule.setAwardId(awardId);
        return strategyRuleDao.queryStrategyRuleValue(strategyRule);
    }

    @Override
    public String queryRuleModelByStrategyId(Long strategyId) {
        return strategyDao.queryRuleModelByStrategyId(strategyId);
    }

    @Override
    public StrategyAwardRuleModelVo queryRuleModelByStrategyIdAndAwardId(Long strategyId, Integer randomAwardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(randomAwardId);
        String ruleModels = strategyAwardDao.queryRuleModels(strategyAward);
        return new StrategyAwardRuleModelVo(ruleModels);
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, String ruleBlack) {
        return queryStrategyRuleValue(strategyId, null, ruleBlack);
    }

    @Override
    public RuleTreeVO queryRuleTreeByTreeId(String treeId) {
        RuleTree ruleTree = ruleTreeDao.queryRuleTreeByTreeId(treeId);
        List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeDao.queryRuleTreeNodeByTreeId(treeId);
        List<RuleTreeNodeLine> ruleTreeNodeLines = ruleTreeNodeLineDao.queryRuleTreeNodeLines(treeId);

        Map<String, List<RuleTreeNodeLineVO>> ruleTreeNodeLineMap = new HashMap<>();
        for (RuleTreeNodeLine ruleTreeNodeLine : ruleTreeNodeLines) {
            List<RuleTreeNodeLineVO> ruleTreeNodeLineVOS = ruleTreeNodeLineMap.computeIfAbsent(ruleTreeNodeLine.getRuleNodeFrom(), k -> new ArrayList<>());
            ruleTreeNodeLineVOS.add(RuleTreeNodeLineVO.builder()
                    .treeId(ruleTreeNodeLine.getTreeId())
                    .ruleNodeFrom(ruleTreeNodeLine.getRuleNodeFrom())
                    .ruleNodeTo(ruleTreeNodeLine.getRuleNodeTo())
                    .ruleLimitType(RuleLimitTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitType()))
                    .ruleLimitValue(RuleLogicCheckTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitValue())).build());
        }

        Map<String, RuleTreeNodeVO> ruleTreeNodeVOMap = new HashMap<>();
        for (RuleTreeNode ruleTreeNode : ruleTreeNodes) {
            ruleTreeNodeVOMap.put(ruleTreeNode.getRuleKey(),
                    RuleTreeNodeVO.builder()
                            .treeId(ruleTreeNode.getTreeId())
                            .ruleKey(ruleTreeNode.getRuleKey())
                            .ruleDesc(ruleTreeNode.getRuleDesc())
                            .ruleValue(ruleTreeNode.getRuleValue())
                            .treeNodeLineVOList(ruleTreeNodeLineMap.get(ruleTreeNode.getRuleKey())).build());
        }


        return RuleTreeVO.builder()
                .treeName(ruleTree.getTreeName())
                .treeId(ruleTree.getTreeId())
                .treeDesc(ruleTree.getTreeDesc())
                .treeNodeMap(ruleTreeNodeVOMap)
                .treeRootRuleNode(ruleTree.getTreeNodeRuleKey()).build();
    }

    @Override
    public void storeStrategyStock(Long strategyId, Integer awardId, Integer awardCountSurplus) {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
        if (!redisService.isExists(cacheKey)) {
            redisService.setAtomicLong(cacheKey, awardCountSurplus);
        }
    }

    @Override
    public String queryRuleValueByStrategyAwardId(Long strategyId, Integer awardId, String ruleModel) {
        StrategyRule strategyRule = new StrategyRule();
        strategyRule.setStrategyId(strategyId);
        strategyRule.setAwardId(awardId);
        strategyRule.setRuleModel(ruleModel);
        return strategyRuleDao.queryStrategyRuleValue(strategyRule);
    }

    @Override
    public Boolean subtractionAwardStock(Long strategyId, Integer awardId) {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
        long decr = redisService.decr(cacheKey);
        if (decr < 0) {
            // 如果库存不足 设为0
            redisService.setAtomicLong(cacheKey, 0);
            return false;
        }

        // 如果扣减成功 设置lock锁 防止库存超卖
        String lockKey = cacheKey + Constants.UNDERLINE + decr;
        return redisService.setNx(lockKey);
    }

    @Override
    public void sendQueue(StrategyAwardStockVO strategyAwardStockVO) {
        String key = Constants.RedisKey.STRATEGY_AWARD_QUEUE_KEY;
        RBlockingQueue<StrategyAwardStockVO> blockingQueue = redisService.getBlockingQueue(key);
        RDelayedQueue<StrategyAwardStockVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(strategyAwardStockVO, 3, TimeUnit.SECONDS);
    }

    @Override
    public StrategyAwardStockVO getAwardQueue() {
        String key = Constants.RedisKey.STRATEGY_AWARD_QUEUE_KEY;
        RBlockingQueue<StrategyAwardStockVO> blockingQueue = redisService.getBlockingQueue(key);
        return blockingQueue.poll();
    }

    @Override
    public Boolean updateAwardStock(StrategyAwardStockVO strategyAwardStockVO) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setAwardId(strategyAwardStockVO.getAwardId());
        strategyAward.setStrategyId(strategyAwardStockVO.getStrategyId());
        return strategyAwardDao.updateAwardStock(strategyAward);
    }

    @Override
    public StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId) {

        // 先从缓存中获取
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_KEY + strategyId + Constants.UNDERLINE + awardId;
        StrategyAwardEntity strategyAwardEntities = redisService.getValue(cacheKey);

        if (strategyAwardEntities != null) {
            return strategyAwardEntities;
        }

        // 从数据库中获取
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setAwardId(awardId);
        strategyAward.setStrategyId(strategyId);
        StrategyAward strategyAwards = strategyAwardDao.queryStrategyAward(strategyAward);
        StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
                .strategyId(strategyAwards.getStrategyId())
                .awardId(strategyAwards.getAwardId())
                .awardCount(strategyAwards.getAwardCount())
                .awardTitle(strategyAwards.getAwardTitle())
                .awardSubtitle(strategyAwards.getAwardSubtitle())
                .awardCountSurplus(strategyAwards.getAwardCountSurplus())
                .awardRate(strategyAwards.getAwardRate())
                .sort(strategyAwards.getSort())
                .build();

        // 保存redis
        redisService.setValue(cacheKey, strategyAwardEntity);
        return strategyAwardEntity ;
    }

}
